package util.reg;

import util.MyArrays;

import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.List;

/**
 * @author Karl
 * @Date 2023/1/9 - 20:27
 */
public class RegFactory{
    private RegFactory(){}

    /**
     * 生成一个响应报文的请求行正则表达式的判断对象
     * @return 判断对象
     */
    public static RegEx createResponseLineReg(){
        return new RegEx(){
            private int status = 0;
            private byte[] buf = new byte[64];
            private int off = 0;
            @Override
            public String enterChar(byte c) {
                switch (status){
                    case 0:
                        if(c=='H'){
                            status++;
                            buf[off++] = c;
                        }
                        break;
                    case 1:
                        if(c=='T') {
                            status++;
                            buf[off++] = c;
                        }
                        else if(c!='H') {
                            status= 0;
                            off = 0;
                        }
                        break;
                    case 2:
                        if(c=='T') {
                            status++;
                            buf[off++] = c;
                        } else if(c=='H') {
                            status= 1;
                            off = 1;
                        }else {
                            status = 0;off = 0;
                        }
                        break;
                    case 3:
                        if(c=='P') {
                            status++;
                            buf[off++] = c;
                        }else if(c=='H') {
                            status= 1;
                            off = 1;
                        }
                        else {
                            status = 0;
                            off = 0;
                        }
                        break;
                    case 4:
                        if(c=='/') {
                            status++;
                            buf[off++] = c;
                        }else if(c=='H') {
                            status= 1;
                            off = 1;
                        }
                        else {
                            status = 0;off = 0;
                        }
                        break;
                    case 5: case 7:
                    case 10:case 11:
                        if(Character.isDigit(c)) {
                            status++;
                            buf[off++] = c;
                        }else if(c=='H') {
                            status= 1;
                            off = 1;
                        }
                        else {
                            status = 0;off = 0;
                        }
                        break;
                    case 6:
                        if(c=='.') {
                            status++;
                            buf[off++] = c;
                        }else if(c=='H') {
                            status= 1;
                            off = 1;
                        }
                        else {
                            status = 0;off = 0;
                        }
                        break;
                    case 8:
                        if(c==' ') {
                            status++;
                            buf[off++] = c;
                        }else if(c=='H') {
                            status= 1;
                            off = 1;
                        }
                        else {
                            status = 0;off = 0;
                        }
                        break;
                    case 9:
                        if(Character.isDigit(c)) {
                            status++;
                            buf[off++] = c;
                        }else if(c=='H') {
                            status= 1;
                            off = 1;
                        }
                        else if(c!=' ') {
                            status = 0;off = 0;
                        }else buf[off++] = c;
                        break;
                    case 12:
                        if(c=='\r'){
                            status++;
                            buf[off++] = c;
                        }else if(isPrintable(c)) {
                            buf[off++] = c;
                        }else{
                            reset();
                        }
                        break;
//                    case 14:
//                        if(isPrintable(c)) {
//                            buf[off++] = c;
//                        }else if(c=='\r'){
//                            buf[off++] = c;
//                            status++;
//                        }else{
//                            reset();
//                        }
//                        break;
                    case 13:
                        if(c=='\n') {
                            status++;
                            buf[off++] = c;
                        }else if(c=='H') {
                            status= 1;
                            off = 1;
                        }
                        else {
                            status = 0;off = 0;
                        }
                }
                return status!=14?null:new String(buf,0,off);
            }

            @Override
            public void reset() {
                status = 0;
                off = 0;
            }
        };
    }


    /**
     * 生成一个求响应头的正则表达式的判断对象
     * @return 正则表达式对象
     */
    public static RegEx createResponseHeaderReg(){
        return null;
    }

    /**
     * 生成一个请求报文的请求行的正则表达式的判断对象
     * @return 正则表达式对象
     */
    public static RegEx createRequestLineReg(){
        return new RegEx() {
            private int status = 0;
            private byte[] buf = new byte[256];
            private int off = 0;
//            private String headLineReg = "[A-Z]{3,7}\\s+[^\\s]+\\s+HTTP\\/[0-9]\\.[0-9]\r\n";

            @Override
            public String enterChar(byte c) {
                try {
                    switch (status){
                        case 0:
                            if(Character.isUpperCase(c)){
                                //小于7个，没问题
                                if(off<7) buf[off++] = c;
                                else{
                                    //超过七个了，把第一位移掉
                                    buf[off] = c;
                                    MyArrays.moveToHead(buf,1,off+1);
                                }
                            }else if(Character.isSpaceChar(c)){
                                buf[off++] = c;
                                status++;
                            }
                            break;
                        case 1:
                            if(Character.isSpaceChar(c)){
                                buf[off++] = c;
                            }else if(isPrintable(c)){
                                buf[off++] = c;
                                status++;
                            }else{
                               reset();
                            }
                            break;
                        case 2:
                            if(Character.isSpaceChar(c)){
                                buf[off++] = c;
                                status++;
                            }else if(isPrintable(c)){
                                buf[off++] = c;
                            }else{
                                reset();
                            }
                            break;
                        case 3:
                            if(Character.isSpaceChar(c)){
                                buf[off++] = c;
                            }else{
                                if(c=='H'){
                                    status++;
                                    buf[off++] = c;
                                }else if(isPrintable(c)){
                                    buf[off++] = c;
                                    traceBack();
                                }else{
                                    reset();
                                }
                            }
                            break;
                        case 4: case 5:
                            if(c=='T') {
                                status++;
                                buf[off++] = c;
                            } else if(isPrintable(c)) {
                                buf[off++] = c;
                                traceBack();
                            }else{
                                reset();
                            }
                            break;
                        case 6:
                            if(c=='P') {
                                status++;
                                buf[off++] = c;
                            }else if(isPrintable(c)) {
                                buf[off++] = c;
                                traceBack();
                            }else{
                                reset();
                            }
                            break;
                        case 7:
                            if(c=='/') {
                                status++;
                                buf[off++] = c;
                            }else if(isPrintable(c)) {
                                buf[off++] = c;
                                traceBack();
                            }else{
                                reset();
                            }
                            break;
                        case 8: case 10:
                            if(Character.isDigit(c)) {
                                status++;
                                buf[off++] = c;
                            }else if(isPrintable(c)) {
                                buf[off++] = c;
                                traceBack();
                            }else{
                                reset();
                            }
                            break;
                        case 9:
                            if(c=='.') {
                                status++;
                                buf[off++] = c;
                            }else if(isPrintable(c)) {
                                buf[off++] = c;
                                traceBack();
                            }else{
                                reset();
                            }
                            break;
                        case 11:
                            if(c=='\r'){
                                status++;
                                buf[off++] = c;
                            }else if(isPrintable(c)) {
                                buf[off++] = c;
                                traceBack();
                            }else reset();
                            break;
                        case 12:
                            if(c=='\n') {
                                status++;
                                buf[off++] = c;
                            }else reset();
                    }
                } catch (Exception e) {
                    reset();
                }
                return status!=13?null:new String(buf,0,off);
            }

            /**
             * 解决例如GET GET http://www.baidu.com HTTP/1.1
             * status 在htt这一块回滚问题
             */
            private void traceBack(){
                int p = 0;
                //找到第一个GET后的空格
                while(p<off&&!Character.isSpaceChar(buf[p++]));
                //找到第一个非空格
                while(p<off&&Character.isSpaceChar(buf[p])) p++;
                int begin = p;
                while(p<off&&!Character.isSpaceChar(buf[p])){
                    if(Character.isUpperCase(buf[p])) p++;
                    else{
                        reset();
                        return;
                    }
                }
                //因为正则表达式要求3-7个大写字符表示请求的方法，如果不是，直接reset
                if(p-begin<3||p-begin>7){
                    reset();
                    return;
                }
                //将第一个GET和后面的空格去掉，第二个GET直接提前，状态status改为2
                MyArrays.moveToHead(buf,begin,off);
                off = off - begin;
                status = 2;
            }

            @Override
            public void reset() {
                status = 0;
                off = 0;
            }
        };
    }


    private static boolean isPrintable(int codePoint){
        return codePoint>31&&codePoint<127;
    }
}
